from typing import List, Optional, Tuple
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_
from fastapi import HTTPException
from datetime import datetime

from app.models.models import Student, Room, RoomAssignment, SwapRequest, Building
from app.services.matching_service import RoommateMatchingService

class DormitoryService:
    def __init__(self, db: Session):
        self.db = db
        self.matching_service = RoommateMatchingService(db)

    def get_available_rooms(self, gender: str, min_beds: int = 1) -> List[Room]:
        """获取可用宿舍列表"""
        return (
            self.db.query(Room)
            .join(Building)
            .filter(
                Building.gender == gender,
                Room.capacity - Room.current_occupancy >= min_beds
            )
            .all()
        )

    def assign_student_to_room(self, student: Student, room: Room) -> RoomAssignment:
        """分配学生到宿舍"""
        # 检查性别是否匹配
        if student.gender != room.building.gender:
            raise HTTPException(status_code=400, detail="性别不匹配")
        
        # 检查房间是否有空位
        if room.current_occupancy >= room.capacity:
            raise HTTPException(status_code=400, detail="房间已满")
        
        # 检查学生是否已有房间
        existing_assignment = (
            self.db.query(RoomAssignment)
            .filter(
                RoomAssignment.student_id == student.id,
                RoomAssignment.is_active == True
            )
            .first()
        )
        if existing_assignment:
            raise HTTPException(status_code=400, detail="学生已有房间分配")
        
        # 创建新的分配记录
        assignment = RoomAssignment(
            student_id=student.id,
            room_id=room.id,
            assigned_at=datetime.now(),
            is_active=True
        )
        
        # 更新房间占用数
        room.current_occupancy += 1
        
        self.db.add(assignment)
        self.db.commit()
        self.db.refresh(assignment)
        
        return assignment

    def remove_student_from_room(self, student: Student) -> None:
        """将学生从宿舍中移除"""
        assignment = (
            self.db.query(RoomAssignment)
            .filter(
                RoomAssignment.student_id == student.id,
                RoomAssignment.is_active == True
            )
            .first()
        )
        
        if not assignment:
            raise HTTPException(status_code=404, detail="未找到有效的房间分配")
        
        # 更新房间占用数
        room = assignment.room
        room.current_occupancy -= 1
        
        # 标记分配为无效
        assignment.is_active = False
        
        self.db.commit()

    def create_swap_request(self, requester: Student, target: Student) -> SwapRequest:
        """创建调宿申请"""
        # 检查两个学生是否都有有效的房间分配
        requester_assignment = (
            self.db.query(RoomAssignment)
            .filter(
                RoomAssignment.student_id == requester.id,
                RoomAssignment.is_active == True
            )
            .first()
        )
        target_assignment = (
            self.db.query(RoomAssignment)
            .filter(
                RoomAssignment.student_id == target.id,
                RoomAssignment.is_active == True
            )
            .first()
        )
        
        if not requester_assignment or not target_assignment:
            raise HTTPException(status_code=400, detail="学生未分配房间")
        
        # 创建调宿申请
        swap_request = SwapRequest(
            requester_id=requester.id,
            target_id=target.id,
            status="pending"
        )
        
        self.db.add(swap_request)
        self.db.commit()
        self.db.refresh(swap_request)
        
        return swap_request

    def process_swap_request(self, swap_request: SwapRequest, approve: bool) -> None:
        """处理调宿申请"""
        if swap_request.status != "pending":
            raise HTTPException(status_code=400, detail="该申请已处理")
        
        if approve:
            # 获取两个学生的房间分配
            requester_assignment = (
                self.db.query(RoomAssignment)
                .filter(
                    RoomAssignment.student_id == swap_request.requester_id,
                    RoomAssignment.is_active == True
                )
                .first()
            )
            target_assignment = (
                self.db.query(RoomAssignment)
                .filter(
                    RoomAssignment.student_id == swap_request.target_id,
                    RoomAssignment.is_active == True
                )
                .first()
            )
            
            # 交换房间
            requester_room_id = requester_assignment.room_id
            target_room_id = target_assignment.room_id
            
            requester_assignment.is_active = False
            target_assignment.is_active = False
            
            new_requester_assignment = RoomAssignment(
                student_id=swap_request.requester_id,
                room_id=target_room_id,
                assigned_at=datetime.now(),
                is_active=True
            )
            new_target_assignment = RoomAssignment(
                student_id=swap_request.target_id,
                room_id=requester_room_id,
                assigned_at=datetime.now(),
                is_active=True
            )
            
            self.db.add_all([new_requester_assignment, new_target_assignment])
        
        # 更新申请状态
        swap_request.status = "approved" if approve else "rejected"
        swap_request.updated_at = datetime.now()
        
        self.db.commit()

    def optimize_room_assignments(self) -> List[Tuple[Student, Room]]:
        """优化宿舍分配"""
        # 获取所有未分配房间的学生
        unassigned_students = (
            self.db.query(Student)
            .outerjoin(RoomAssignment)
            .filter(
                or_(
                    RoomAssignment.id == None,
                    RoomAssignment.is_active == False
                )
            )
            .all()
        )
        
        assignments = []
        for student in unassigned_students:
            # 获取可用房间
            available_rooms = self.get_available_rooms(student.gender)
            if not available_rooms:
                continue
            
            best_room = None
            best_score = -1
            
            for room in available_rooms:
                # 计算与房间现有学生的平均匹配分数
                current_occupants = (
                    self.db.query(Student)
                    .join(RoomAssignment)
                    .filter(
                        RoomAssignment.room_id == room.id,
                        RoomAssignment.is_active == True
                    )
                    .all()
                )
                
                if not current_occupants:
                    # 空房间，直接分配
                    best_room = room
                    break
                
                # 计算与现有室友的平均匹配分数
                total_score = sum(
                    self.matching_service.calculate_matching_score(student, occupant)
                    for occupant in current_occupants
                )
                avg_score = total_score / len(current_occupants)
                
                if avg_score > best_score:
                    best_score = avg_score
                    best_room = room
            
            if best_room:
                assignments.append((student, best_room))
                self.assign_student_to_room(student, best_room)
        
        return assignments 